package storage

import (
	"bytes"
	bolt "gitee.com/wanttobeamaster/bbolt"
	"github.com/fagongzi/util/format"
	"github.com/fagongzi/util/hack"
)

type boltKVEngine struct {
	db *bolt.DB
}

func newBoltKVEngine(db *bolt.DB) KVEngine {
	db.Update(func(tx *bolt.Tx) error {
		_, err := tx.CreateBucketIfNotExists([]byte("boltKVBucket"))
		if err != nil {
			return err
		}
		return nil
	})
	return &boltKVEngine{
		db: db,
	}
}

func (e *boltKVEngine) RangeDelete(start, end []byte) error {
	e.db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("boltKVBucket"))
		c := b.Cursor()
		for k, _ := c.Seek(start); k != nil && bytes.Compare(k, end) <= 0; k, _ = c.Next() {
			err := b.Delete(k)
			if err != nil {
				return err
			}
		}
		return nil
	})
	return nil
}

func (e *boltKVEngine) Set(key, value []byte) error {
	e.db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("boltKVBucket"))
		err := b.Put(key, value)
		if err != nil {
			return err
		}
		return nil
	})
	return nil
}

func (e *boltKVEngine) MSet(keys [][]byte, values [][]byte) error {
	return e.db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("boltKVBucket"))
		for i := 0; i < len(keys); i++ {
			err := b.Put(keys[i], values[i])
			if err != nil {
				return err
			}
		}
		return nil
	})
}

func (e *boltKVEngine) Get(key []byte) ([]byte, error) {
	var v []byte

	e.db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("boltKVBucket"))
		v = b.Get(key)
		return nil
	})

	return v, nil
}

func (e *boltKVEngine) IncrBy(key []byte, incrment int64) (int64, error) {
	var value int64
	var v []byte

	e.db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("boltKVBucket"))
		v = b.Get(key)
		return nil
	})

	if len(v) != 0 {
		value, _ = format.ParseStrInt64(hack.SliceToString(v))
	}

	value = value + incrment

	e.db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("boltKVBucket"))
		err := b.Put(key, format.Uint64ToBytes(uint64(value)))
		if err != nil {
			return err
		}
		return nil
	})

	return value, nil
}

func (e *boltKVEngine) DecrBy(key []byte, incrment int64) (int64, error) {
	var value int64
	var v []byte

	e.db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("boltKVBucket"))
		v = b.Get(key)
		return nil
	})

	if len(v) != 0 {
		value, _ = format.ParseStrInt64(hack.SliceToString(v))
	}

	value = value - incrment

	e.db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("boltKVBucket"))
		err := b.Put(key, format.Uint64ToBytes(uint64(value)))
		if err != nil {
			return err
		}
		return nil
	})

	return value, nil
}

func (e *boltKVEngine) GetSet(key, value []byte) ([]byte, error) {
	return nil, nil
}

func (e *boltKVEngine) Append(key, value []byte) (int64, error) {
	var v []byte

	e.db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("boltKVBucket"))
		v = b.Get(key)
		return nil
	})

	v = append(v, value...)

	e.db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("boltKVBucket"))
		err := b.Put(key, v)
		return err
	})

	return 0, nil
}

func (e *boltKVEngine) SetNX(key, value []byte) (int64, error) {
	return 0, nil
}

func (e *boltKVEngine) StrLen(key []byte) (int64, error) {
	return 0, nil
}

func (e *boltKVEngine) NewWriteBatch() WriteBatch {
	return newBoltWriteBatch()
}

func (e *boltKVEngine) Write(wb WriteBatch) error {
	return e.db.Batch(func(tx *bolt.Tx) error {
		var err error
		// xiaoxiao : defaultBucket
		bucket := tx.Bucket([]byte("boltKVBucket"))
		for _, opt := range wb.(*boltWriteBatch).opts {
			curKey, curVal := opt.key, opt.value
			if opt.isDelete {
				err = bucket.Delete(curKey)
			} else {
				err = bucket.Put(curKey, curVal)
			}
			if err != nil {
				return err
			}
		}
		return nil
	})
}
